package com.fengye.security.jwt.process;

import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @Author sky
 * @Email <mailto:fengyexjtu@126.com>
 * @Date 2020/7/27 11:06 下午
 * @Description 用户认证并赋予权限
 */
public class JwtAuthenticationProvider implements AuthenticationProvider {

    private final JwtUserDetailsService userDetailsService;

    private final PasswordEncoder passwordEncoder;

    public JwtAuthenticationProvider(JwtUserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * 鉴权具体逻辑
     *
     * @param authentication 用户信息
     * @return {@link Authentication}
     * @throws AuthenticationException 认证失败
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    	// 1. 根据用户名获取用户的详细信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName());
        // 2. 转换authentication 认证时会先调用support方法,受支持才会调用,所以能强转
        JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
        // 3. 检查账户状态
        defaultCheck(userDetails);
        // 4. 校验用户名密码 具体逻辑
        additionalAuthenticationChecks(userDetails, jwtAuthenticationToken);
        // 5. 构造已认证的authentication
        JwtAuthenticationToken authenticatedToken = new JwtAuthenticationToken(userDetails, jwtAuthenticationToken.getCredentials(),
                userDetails.getAuthorities());
        // 6.
        authenticatedToken.setDetails(jwtAuthenticationToken.getDetails());

        // 7. 这一步在manager中进行
        // SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);
        return authenticatedToken;
    }

    /**
     * 是否支持当前类
     *
     * @param authentication 请求用户信息封装对象
     * @return JwtLoginToken是否为authentication的父类
     */
    public boolean supports(Class<?> authentication) {
        return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
    }

    /**
     * 一些默认信息的检查
     *
     * @param user 数据库中的用户信息
     */
    private void defaultCheck(UserDetails user) {
        // 账号是否被锁定
        if (!user.isAccountNonLocked()) {
            throw new LockedException("User account is locked");
        }

        // 账号是否为启用
        if (!user.isEnabled()) {
            throw new DisabledException("User is disabled");
        }

        // 账户过期
        if (!user.isAccountNonExpired()) {
            throw new AccountExpiredException("User account has expired");
        }
    }

    /**
     * 检查密码是否正确
     *
     * @param userDetails    封装了用户名和密码的用户信息的对象
     * @param authentication 请求登录的用户信息
     * @throws AuthenticationException 认证失败异常
     */
    private void additionalAuthenticationChecks(UserDetails userDetails, Authentication authentication)
            throws AuthenticationException {
		/*
		请求登录的用户非空验证
		 */
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException("密码不能为空");
        }

		/*
		解析请求登录用户的密码
		 */
        String presentedPassword = authentication.getCredentials().toString();

		/*
		比较用户的密码userDetails.getPassword()和请求用户的密码是否相同
		 */
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            throw new BadCredentialsException("密码不正确");
        }
    }

}